package fr.umlv.m2.jee.spij.service.registration.impl;

import fr.umlv.m2.jee.spij.persistence.actor.*;
import fr.umlv.m2.jee.spij.persistence.actor.dao.*;
import fr.umlv.m2.jee.spij.persistence.education.*;
import fr.umlv.m2.jee.spij.persistence.education.dao.HibernateUnitDAO;
import fr.umlv.m2.jee.spij.persistence.schooling.Course;
import fr.umlv.m2.jee.spij.persistence.schooling.Registration;
import fr.umlv.m2.jee.spij.persistence.schooling.Training;
import fr.umlv.m2.jee.spij.persistence.schooling.dao.HibernateCourseDAO;
import fr.umlv.m2.jee.spij.persistence.schooling.dao.HibernateRegistrationDAO;
import fr.umlv.m2.jee.spij.persistence.schooling.dao.HibernateTrainingDAO;
import fr.umlv.m2.jee.spij.service.ldap.authentification.DisplayActor;
import fr.umlv.m2.jee.spij.service.registration.DisplayRegistration;
import fr.umlv.m2.jee.spij.service.registration.IDisplayRegistrationService;
import fr.umlv.m2.jee.spij.service.unit.DisplayUnit;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.faces.model.SelectItem;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
@Service("defaultDisplayRegistrationService")
public class DefaultDisplayRegistrationService implements
        IDisplayRegistrationService {

    /**
     * the registration DAO.
     */
    @Autowired
    private HibernateRegistrationDAO registrationDAO;

    /**
     * the training DAO.
     */
    @Autowired
    private HibernateTrainingDAO trainingDAO;

    /**
     * the course DAO.
     */
    @Autowired
    private HibernateCourseDAO courseDAO;

    /**
     * the unit DAO.
     */
    @Autowired
    private HibernateUnitDAO unitDAO;

    /**
     * the student lesson DAO.
     */
    @Autowired
    private HibernateStudentLessonDAO studentLessonDAO;

    /**
     * the student field word DAO.
     */
    @Autowired
    private HibernateStudentFieldWorkDAO studentFieldWorkDAO;

    /**
     * the student partial DAO.
     */
    @Autowired
    private HibernateStudentPartialDAO studentPartialDAO;

    /**
     * the student project DAO.
     */
    @Autowired
    private HibernateStudentProjectDAO studentProjectDAO;

    /**
     * the student tutorial DAO.
     */
    @Autowired
    private HibernateStudentTutorialDAO studentTutorialDAO;

    /**
     * .
     *
     * @param currentUser
     * @param context
     *
     * @return
     */
    @Transactional(readOnly = true)
    @Override
    public List<DisplayRegistration> getAllDisplayRegistrationForUser(
            DisplayActor currentUser, MessageContext context) {
        final List<Registration> registrations;

        if (currentUser.isStudent())
            registrations = registrationDAO.findByLdapActor(currentUser
                    .getLogin());
        else
            registrations = registrationDAO.findAll();

        final List<DisplayRegistration> displayRegistrations = new ArrayList<DisplayRegistration>();

        if (registrations == null) {
            context.addMessage(new MessageBuilder().error()
                    .code("registrations.empty").build());
        } else {
            for (Registration registration : registrations) {
                DisplayRegistration displayRegistration = new DisplayRegistration(
                        registration, trainingDAO.findById(
                                registration.getTraining()).getName());
                if (registration.getCourse() != null) {
                    displayRegistration.setCourse(courseDAO.findById(
                            registration.getCourse()).getName());
                }
                displayRegistrations.add(displayRegistration);
            }
        }
        return displayRegistrations;
    }

    /**
     * @param id
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = false)
    public boolean deleteRegistrationById(long id, final MessageContext context) {
        registrationDAO.remove(registrationDAO.findById(id));
        return true;
    }

    /**
     * @param createdRegistration
     * @param student
     * @param trainingName
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = false)
    public boolean createRegistration(DisplayRegistration createdRegistration,
                                      String student, String trainingName, MessageContext context) {
        Registration registration = new Registration();
        registration.setLogin(student);
        Training training = trainingDAO.findByName(trainingName);
        registration.setTraining(training.getId());

        final Interval year = new Interval(createdRegistration.getStartTime()
                .getTime(), createdRegistration.getEndTime().getTime());
        registration.setYear(year);
        registrationDAO.persist(registration);

        training.addRegistration(registration);
        trainingDAO.merge(training);
        return true;
    }

    /**
     * @param registration
     * @param courseName
     *
     * @return
     */
    @Override
    @Transactional(readOnly = true)
    public DisplayRegistration updateUnitList(DisplayRegistration registration,
                                              String courseName) {
        Course course = courseDAO.findByName(courseName);
        registration.setCourse(courseName);
        registration.setNbOption(""
                + (course.getNbMatter() - course.getRequiredUnits().size()));
        if (!registration.getCourseUnits().isEmpty())
            registration.getCourseUnits().clear();
        for (Unit units : course.getOptionalUnits()) {
            registration.addCourseUnits(new SelectItem(units.getName(), units
                    .getName()));
        }

        for (Unit unit : course.getRequiredUnits()) {
            registration.addRequiredUnit(unit.getName());
        }
        return registration;
    }

    /**
     * @param courseName
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = true)
    public List<DisplayUnit> getAllDisplayUnitByCourse(String courseName,
                                                       MessageContext context) {
        Course course = courseDAO.findByName(courseName);
        final List<Unit> unitList = course.getRequiredUnits();
        final List<DisplayUnit> displayUnitList = new ArrayList<DisplayUnit>(
                unitList.size());

        if (unitList.isEmpty()) {
            context.addMessage(new MessageBuilder().info().code("units.empty")
                    .build());
        } else {
            for (Unit unit : unitList) {
                displayUnitList.add(new DisplayUnit(unit));
            }
        }
        return displayUnitList;
    }

    /**
     * @param courseName
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = true)
    public List<DisplayUnit> getAllDisplayOptionalUnitByCourse(
            String courseName, MessageContext context) {
        Course course = courseDAO.findByName(courseName);
        final List<Unit> unitList = course.getOptionalUnits();
        final List<DisplayUnit> displayUnitList = new ArrayList<DisplayUnit>(
                unitList.size());

        if (unitList.isEmpty()) {
            context.addMessage(new MessageBuilder().info().code("units.empty")
                    .build());
        } else {
            for (Unit unit : unitList) {
                displayUnitList.add(new DisplayUnit(unit));
            }
        }
        return displayUnitList;
    }

    /**
     * @param registration
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = false)
    public boolean validateRegistration(DisplayRegistration registration,
                                        MessageContext context) {
        if (registration.nbOfUnits() != Integer.parseInt(registration
                .getNbOption())) {
            context.addMessage(new MessageBuilder().info()
                    .code("units.tooMany").build());
            return false;
        }
        StudentTutorial st = null;
        StudentPartial sp = null;
        StudentLesson sl = null;
        StudentProject spr = null;
        StudentFieldWork sf = null;
        Registration reg = registrationDAO.findById(registration.getId());
        reg.setCourse(courseDAO.findByName(registration.getCourse()).getId());
        reg.setTraining(trainingDAO.findByName(registration.getTraining())
                .getId());
        for (String unit : registration.getRequiredUnits()) {
            Unit u = unitDAO.findByName(unit);
            reg.addUnit(u);
            for (Lesson lesson : u.getLessons()) {
                sl = new StudentLesson();
                sl.setLesson(lesson.getId());
                sl.setStudentLogin(reg.getLogin());
                studentLessonDAO.persist(sl);
            }
            for (Partial partial : u.getPartials()) {
                sp = new StudentPartial();
                sp.setPartial(partial.getId());
                sp.setStudentLogin(reg.getLogin());
                studentPartialDAO.persist(sp);
            }
            for (Project project : u.getProjects()) {
                spr = new StudentProject();
                spr.setProject(project.getId());
                spr.setStudentLogin(reg.getLogin());
                studentProjectDAO.persist(spr);
            }
            for (FieldWork fieldWork : u.getFieldWorks()) {
                sf = new StudentFieldWork();
                sf.setFieldWork(fieldWork.getId());
                sf.setStudentLogin(reg.getLogin());
                studentFieldWorkDAO.persist(sf);
            }
            for (Tutorial tutorial : u.getTutorials()) {
                st = new StudentTutorial();
                st.setTutorial(tutorial.getId());
                st.setStudentLogin(reg.getLogin());
                studentTutorialDAO.persist(st);
            }
        }
        for (String stringUnit : registration.getUnits()) {
            Unit u = unitDAO.findByName(stringUnit);
            reg.addUnit(u);

            for (Lesson lesson : u.getLessons()) {
                sl = new StudentLesson();
                sl.setLesson(lesson.getId());
                sl.setStudentLogin(reg.getLogin());
                studentLessonDAO.persist(sl);
            }
            for (Partial partial : u.getPartials()) {
                sp = new StudentPartial();
                sp.setPartial(partial.getId());
                sp.setStudentLogin(reg.getLogin());
                studentPartialDAO.persist(sp);
            }
            for (Project project : u.getProjects()) {
                spr = new StudentProject();
                spr.setProject(project.getId());
                spr.setStudentLogin(reg.getLogin());
                studentProjectDAO.persist(spr);
            }
            for (FieldWork fieldWork : u.getFieldWorks()) {
                sf = new StudentFieldWork();
                sf.setFieldWork(fieldWork.getId());
                sf.setStudentLogin(reg.getLogin());
                studentFieldWorkDAO.persist(sf);
            }
            for (Tutorial tutorial : u.getTutorials()) {
                st = new StudentTutorial();
                st.setTutorial(tutorial.getId());
                st.setStudentLogin(reg.getLogin());
                studentTutorialDAO.persist(st);
            }
        }
        reg.setDate(new DateTime());
        registrationDAO.merge(reg);
        return true;
    }

    /**
     * @param registration
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = true)
    public List<DisplayUnit> getAllDisplayUnitForStudent(
            DisplayRegistration registration, MessageContext context) {
        Registration reg = registrationDAO.findById(registration.getId());
        final List<Unit> unitList = reg.getUnits();
        final List<DisplayUnit> displayUnitList = new ArrayList<DisplayUnit>(
                unitList.size());

        if (unitList.isEmpty()) {
            context.addMessage(new MessageBuilder().info().code("units.empty")
                    .build());
        } else {
            for (Unit unit : unitList) {
                displayUnitList.add(new DisplayUnit(unit));
            }
        }
        return displayUnitList;
    }
}
